/**
 * @widoc $.fn.dropdown
 * @namespace comp
 * @des 显示弹出层
 * @type function
 * @param {object} opts 弹出层配置参数
 * @param {object} opts.cont 弹出层的 dom 字符串或 jquery 对象
 * @param {string=} opts.pos 优先选择的弹出方向，默认为 'bottom-left'（贴下边和左边）
 * @param {boolean=} opts.adjust 是否允许弹出方向自适应，默认为 true
 * @param {boolean=} opts.noDestroy 关闭时是否仅隐藏，默认为 true，即关闭时不销毁弹窗
 * @param {boolean=} opts.show 初始化时是否直接打开，默认为 true
 * @param {boolean=} opts.modal 是否为模态遮罩层，默认为 true
 * @param {boolean=} opts.context 是否为右键触发弹出，默认为 false。当设置为 true 时，右键点击时显示，自动设置pos为bottom-left，adjust为true，show为false，modal为true。
 * @param {function()=} opts.beforeClose 关闭下拉框前的回调函数
 * @param {function()=} opts.beforeShow 显示下拉框前的回调函数，右键菜单显示时，传入参数 event
 * @return {object|undefined} obj 返回对象
 * @rtn {function} obj.show() 显示弹出框
 * @rtn {function} obj.close() 关闭弹出层
 * @rtn {function} obj.isShown() 是否已经打开
 */
$.fn.dropdown=function(opts){
    var el=$(this)// 弹出参照元素
        ,overlayEl// 弹出遮罩层
        ,dropdownEl;// 弹出内容主体
    opts=$.extend({
        cont:''
        ,pos:'bottom-left'
        ,adjust:true
        ,noDestroy:true
        ,show: true
        ,modal: true
        ,context: false
        ,beforeClose: $.noop
        ,beforeShow: $.noop
    },opts);
    if(opts.context){// 右键弹出
        opts.show=false;
        opts.adjust=true;
        opts.modal=true;
        opts.pos='bottom-left';
    }
    var _returnObj={};
    var _doc=$(document);
    var dpGuid=$.generateGuid('dp');
    var bScrolling=false,// 标记是否已经在滚动（为避免mousewheel及onscroll事件重复触发）
        oElBCR,// 记录触发滚动前参照元素原始位置，若滚动引起位置变化将关闭弹出层
        oChargeTimer;
    var fOnDocWheel=function(e){
        // retest 为 true 时，可能由于滚动默认行为还未执行，导致元素位置未发生改变，需要延迟 100ms（暂定） 重新计算
        var fChargeEl=function(retest){
            if(oChargeTimer){
                clearTimeout(oChargeTimer);
                oChargeTimer=undefined;
            }
            if(oElBCR){// dropdown 未隐藏时才需判断
                var oNewElBCR=el[0].getBoundingClientRect();
                if(oNewElBCR.left != oElBCR.left || oNewElBCR.top != oElBCR.top) {// 参照元素位置发生了变化
                    oElBCR = oNewElBCR;
                    _close();
                }else if(retest){
                    oChargeTimer=setTimeout(fChargeEl,100);
                }
            }
        };
        bScrolling=true;
        fChargeEl(true);
    };
    // document 捕获 scroll 事件，以便在下拉框外的其他元素发生滚动时根据 el 的位置决定是否需要隐藏下拉框
    var fOnDocScroll = function(e){
        var oNewElBCR;
        if(bScrolling){
            bScrolling=false;
        }else{
            oNewElBCR=el[0].getBoundingClientRect();
            if(oNewElBCR.left != oElBCR.left || oNewElBCR.top != oElBCR.top) {// 参照元素位置发生了变化
                oElBCR = oNewElBCR;
                _close();
            }
        }
    };
    var _addListener=function(){
        bScrolling=false;
        oElBCR=el[0].getBoundingClientRect();// 记录参照元素的初始位置
        // 由于 jquery 不支持捕获阶段的事件绑定，且 scroll 不会冒泡，因此只能使用原生方法在捕获阶段捕捉 scroll 事件
        // IE8 不支持捕获阶段的事件绑定，因此鼠标拖拽滚动条不会隐藏下拉框
        if(_doc[0].addEventListener){
            _doc[0].addEventListener('scroll', fOnDocScroll, true);
        }
        if(navigator.userAgent.indexOf("Firefox")>0){// 火狐浏览器
            _doc[0].addEventListener('DOMMouseScroll',fOnDocWheel,false);
        }else{// 其他浏览器
            _doc.on('mousewheel.dp'+dpGuid,fOnDocWheel);
        }
        // 点击关闭弹出层
        _doc.on('click.dpout'+dpGuid,function(e){
            var tarEl=$(e.target);
            if(e.type=='click'){// 点击在其他元素上
                if(tarEl.closest(el).length==0 && tarEl.closest('.overlay').length==0){
                    _close();
                }
            }
        });
        $(window).on('resize.dp'+dpGuid, _close);// 窗体发生 resize 时关闭弹出层
    };
    var _removeListener=function(){
        oElBCR=undefined;
        if(_doc[0].addEventListener){
            _doc[0].removeEventListener('scroll', fOnDocScroll, true);
        }
        if(navigator.userAgent.indexOf("Firefox")>0){// 火狐浏览器
            _doc[0].removeEventListener('DOMMouseScroll',fOnDocWheel);
        }else{// 其他浏览器
            _doc.off('mousewheel.dp'+dpGuid);
        }
        _doc.off('click.dpout'+dpGuid);
        $(window).off('resize.dp'+dpGuid);
    };
    var _close=function(){
        if(!overlayEl) return;
        opts.beforeClose();
        overlayEl.css('visibility','hidden');
        dropdownEl.css('visibility','hidden');
        if(!opts.noDestroy){
            _destroy();
        }
        _removeListener();
    };
    var _destroy=function(){
        if(!overlayEl) return;
        overlayEl.remove();
        overlayEl.off('click.dropdown');
        _removeListener();
    };
    var _show=function(e){
        if(!overlayEl || _isShown()) return;
        opts.beforeShow(e);
        //            if(!overlayEl.parent('body').length) $('body').append(overlayEl);
        overlayEl.css('visibility','hidden');
        var elBCR,adaptObj;
        if(e==undefined){
            elBCR=el[0].getBoundingClientRect();
        }else{
            elBCR={left: e.clientX,right:e.clientX,top:e.clientY,bottom: e.clientY};
        }
        adaptObj = $.adaptElement(elBCR,
            //, _doc.width(), _doc.height()
            // 以下为兼容 ie8，ie8 不支持 innerWidth、innerHeight；当主体元素是绝对定位时，会导致 body 高度为 0，因此多加 || Infinity
            (window.innerWidth||Math.min(document.documentElement.clientWidth,document.body.clientWidth||Infinity))-WICONF.scrollsize,
            (window.innerHeight||Math.min(document.documentElement.clientHeight,document.body.clientHeight||Infinity))-WICONF.scrollsize,
            dropdownEl.outerWidth(), dropdownEl.outerHeight(),
            opts.pos, opts.adjust);
        var cssObj = adaptObj[0];
        cssObj['visibility']='';
        if(!opts.context){
            var pos0 = adaptObj[1].split('-')[0];
            // TODO 可能内容宽度比 miniWidth 小
            if(pos0 == 'top' || pos0 =='bottom'){
                cssObj['min-width']=el.outerWidth()+'px'
            }else{
                cssObj['min-height']=el.outerHeight()+'px'
            }
        }
        dropdownEl.css(cssObj);
        overlayEl.css('visibility','');
        //if(!opts.modal){// 非模态时在组件外滚动关闭下拉框
        _addListener();
        //}
    };
    var _init=function(){
        overlayEl=$('<div class="overlay'+(opts.modal ? ' overlay-modal':'')+'" style="visibility:hidden;"></div>');// 是否是模态弹出层
        dropdownEl=$('<div class="dropdown-box" style="visibility:hidden;"></div>');// 调节位置后显示
        dropdownEl.append(opts.cont);
        overlayEl.append(dropdownEl);
        if(opts.context){// 右键菜单
            el.on('contextmenu',function(e){
                e.preventDefault();// 禁用浏览器右键菜单
                _show(e);
            });
        }
        opts.modal && overlayEl.on('click.dropdown',function(e){// 模态弹出层点击遮罩层关闭
            if($(e.target).parent('body').length>0){
                _close();
            }
        });
        if(!overlayEl.parent('body').length) $('body').append(overlayEl);
        opts.show!=false && _show();
    };
    var _isShown=function(){
        return dropdownEl.css('visibility')!='hidden';
    };
    _returnObj.show=_show;
    _returnObj.close=_close;
    _returnObj.destroy=_destroy;
    _returnObj.isShown=_isShown;
    _destroy();
    _init();
    return _returnObj;
};